/*
 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package com.sun.corba.se.impl.interceptors;

import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.LocalObject;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.NO_RESOURCES;
import org.omg.CORBA.NVList;
import org.omg.CORBA.Object;
import org.omg.CORBA.Policy;
import org.omg.CORBA.TypeCode;

import org.omg.PortableServer.Servant;

import org.omg.IOP.TaggedProfile;
import org.omg.IOP.ServiceContext;

import org.omg.Dynamic.Parameter;

import org.omg.PortableInterceptor.InvalidSlot;
import org.omg.PortableInterceptor.ServerRequestInfo;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.PortableInterceptor.SUCCESSFUL;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.omg.PortableInterceptor.TRANSPORT_RETRY;
import org.omg.PortableInterceptor.USER_EXCEPTION;

import com.sun.corba.se.spi.oa.ObjectAdapter;
import com.sun.corba.se.spi.presentation.rmi.StubAdapter;

import com.sun.corba.se.impl.protocol.giopmsgheaders.ReplyMessage;

import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.ior.ObjectKeyTemplate;
import com.sun.corba.se.spi.ior.ObjectAdapterId;

import com.sun.corba.se.spi.protocol.CorbaMessageMediator;

import java.util.*;

/**
 * Implementation of the ServerRequestInfo interface as specified in
 * orbos/99-12-02 section 5.4.3.
 */
public final class ServerRequestInfoImpl
    extends RequestInfoImpl
    implements ServerRequestInfo {

  // The available constants for startingPointCall
  static final int CALL_RECEIVE_REQUEST_SERVICE_CONTEXT = 0;

  // The available constants for intermediatePointCall.  The default (0)
  // is receive_request, but can be set to none on demand.
  static final int CALL_RECEIVE_REQUEST = 0;
  static final int CALL_INTERMEDIATE_NONE = 1;

  // The available constants for endingPointCall
  static final int CALL_SEND_REPLY = 0;
  static final int CALL_SEND_EXCEPTION = 1;
  static final int CALL_SEND_OTHER = 2;

  //////////////////////////////////////////////////////////////////////
  //
  // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
  //
  //////////////////////////////////////////////////////////////////////

  // Set to true if the server ending point raised ForwardRequest at some
  // point in the ending point.
  private boolean forwardRequestRaisedInEnding;

  // Sources of server request information:
  private CorbaMessageMediator request;
  private java.lang.Object servant;
  private byte[] objectId;
  private ObjectKeyTemplate oktemp;

  // Information cached from calls to oktemp
  private byte[] adapterId;
  private String[] adapterName;

  private ArrayList addReplyServiceContextQueue;
  private ReplyMessage replyMessage;
  private String targetMostDerivedInterface;
  private NVList dsiArguments;
  private Any dsiResult;
  private Any dsiException;
  private boolean isDynamic;
  private ObjectAdapter objectAdapter;
  private int serverRequestId;

  // Cached information:
  private Parameter[] cachedArguments;
  private Any cachedSendingException;
  // key = Integer, value = IOP.ServiceContext.
  private HashMap cachedRequestServiceContexts;
  // key = Integer, value = IOP.ServiceContext.
  private HashMap cachedReplyServiceContexts;

  //////////////////////////////////////////////////////////////////////
  //
  // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
  //
  //////////////////////////////////////////////////////////////////////


  /**
   * Reset the info object so that it can be reused for a retry,
   * for example.
   */
  void reset() {
    super.reset();

    // Please keep these in the same order as declared above.

    forwardRequestRaisedInEnding = false;

    request = null;
    servant = null;
    objectId = null;
    oktemp = null;

    adapterId = null;
    adapterName = null;

    addReplyServiceContextQueue = null;
    replyMessage = null;
    targetMostDerivedInterface = null;
    dsiArguments = null;
    dsiResult = null;
    dsiException = null;
    isDynamic = false;
    objectAdapter = null;
    serverRequestId = myORB.getPIHandler().allocateServerRequestId();

    // reset cached attributes:
    cachedArguments = null;
    cachedSendingException = null;
    cachedRequestServiceContexts = null;
    cachedReplyServiceContexts = null;

    startingPointCall = CALL_RECEIVE_REQUEST_SERVICE_CONTEXT;
    intermediatePointCall = CALL_RECEIVE_REQUEST;
    endingPointCall = CALL_SEND_REPLY;
  }

    /*
     **********************************************************************
     * Access protection
     **********************************************************************/

  // Method IDs for all methods in ServerRequestInfo.  This allows for a
  // convenient O(1) lookup for checkAccess().
  protected static final int MID_SENDING_EXCEPTION = MID_RI_LAST + 1;
  protected static final int MID_OBJECT_ID = MID_RI_LAST + 2;
  protected static final int MID_ADAPTER_ID = MID_RI_LAST + 3;
  protected static final int MID_TARGET_MOST_DERIVED_INTERFACE
      = MID_RI_LAST + 4;
  protected static final int MID_GET_SERVER_POLICY = MID_RI_LAST + 5;
  protected static final int MID_SET_SLOT = MID_RI_LAST + 6;
  protected static final int MID_TARGET_IS_A = MID_RI_LAST + 7;
  protected static final int MID_ADD_REPLY_SERVICE_CONTEXT
      = MID_RI_LAST + 8;
  protected static final int MID_SERVER_ID = MID_RI_LAST + 9;
  protected static final int MID_ORB_ID = MID_RI_LAST + 10;
  protected static final int MID_ADAPTER_NAME = MID_RI_LAST + 11;

  // ServerRequestInfo validity table (see ptc/00-08-06 table 21-2).
  // Note: These must be in the same order as specified in contants.
  private static final boolean validCall[][] = {
      // LEGEND:
      // r_rsc = receive_request_service_contexts
      // r_req = receive_request
      // s_rep = send_reply
      // s_exc = send_exception
      // s_oth = send_other
      //
      // A true value indicates call is valid at specified point.
      // A false value indicates the call is invalid.
      //
      // NOTE: If the order or number of columns change, update
      // checkAccess() accordingly.
      //
      //                              { r_rsc, r_req, s_rep, s_exc, s_oth }
      // RequestInfo methods:
        /*request_id*/                  {true, true, true, true, true},
        /*operation*/                   {true, true, true, true, true},
        /*arguments*/                   {false, true, true, false, false},
        /*exceptions*/                  {false, true, true, true, true},
        /*contexts*/                    {false, true, true, true, true},
        /*operation_context*/           {false, true, true, false, false},
        /*result*/                      {false, false, true, false, false},
        /*response_expected*/           {true, true, true, true, true},
        /*sync_scope*/                  {true, true, true, true, true},
        /*reply_status*/                {false, false, true, true, true},
        /*forward_reference*/           {false, false, false, false, true},
        /*get_slot*/                    {true, true, true, true, true},
        /*get_request_service_context*/ {true, true, true, true, true},
        /*get_reply_service_context*/   {false, false, true, true, true},
      //
      // ServerRequestInfo methods::
        /*sending_exception*/           {false, false, false, true, false},
        /*object_id*/                   {false, true, true, true, true},
        /*adapter_id*/                  {false, true, true, true, true},
        /*target_most_derived_inte...*/ {false, true, false, false, false},
        /*get_server_policy*/           {true, true, true, true, true},
        /*set_slot*/                    {true, true, true, true, true},
        /*target_is_a*/                 {false, true, false, false, false},
        /*add_reply_service_context*/   {true, true, true, true, true},
        /*orb_id*/                      {false, true, true, true, true},
        /*server_id*/                   {false, true, true, true, true},
        /*adapter_name*/                {false, true, true, true, true}
  };

    /*
     **********************************************************************
     * Public interfaces
     **********************************************************************/

  /**
   * Creates a new ServerRequestInfo implementation.
   * The constructor is package scope since no other package need create
   * an instance of this class.
   */
  ServerRequestInfoImpl(ORB myORB) {
    super(myORB);
    startingPointCall = CALL_RECEIVE_REQUEST_SERVICE_CONTEXT;
    intermediatePointCall = CALL_RECEIVE_REQUEST;
    endingPointCall = CALL_SEND_REPLY;
    serverRequestId = myORB.getPIHandler().allocateServerRequestId();
  }

  /**
   * Any containing the exception to be returned to the client.
   */
  public Any sending_exception() {
    checkAccess(MID_SENDING_EXCEPTION);

    if (cachedSendingException == null) {
      Any result = null;

      if (dsiException != null) {
        result = dsiException;
      } else if (exception != null) {
        result = exceptionToAny(exception);
      } else {
        // sending_exception should not be callable if both dsiException
        // and exception are null.
        throw wrapper.exceptionUnavailable();
      }

      cachedSendingException = result;
    }

    return cachedSendingException;
  }

  /**
   * The opaque object_id describing the target of the operation invocation.
   */
  public byte[] object_id() {
    checkAccess(MID_OBJECT_ID);

    if (objectId == null) {
      // For some reason, we never set object id.  This could be
      // because a servant locator caused a location forward or
      // raised an exception.  As per ptc/00-08-06, section 21.3.14,
      // we throw NO_RESOURCES
      throw stdWrapper.piOperationNotSupported6();
    }

    // Good citizen: In the interest of efficiency, we will assume
    // interceptors will not change the resulting byte[] array.
    // Otherwise, we would need to make a clone of this array.

    return objectId;
  }

  private void checkForNullTemplate() {
    if (oktemp == null) {
      // For some reason, we never set the ObjectKeyTemplate
      // because a servant locator caused a location forward or
      // raised an exception.  As per ptc/00-08-06, section 21.3.14,
      // we throw NO_RESOURCES
      throw stdWrapper.piOperationNotSupported7();
    }
  }

  public String server_id() {
    checkAccess(MID_SERVER_ID);
    checkForNullTemplate();

    // Good citizen: In the interest of efficiency, we will assume
    // interceptors will not change the resulting byte[] array.
    // Otherwise, we would need to make a clone of this array.

    return Integer.toString(oktemp.getServerId());
  }

  public String orb_id() {
    checkAccess(MID_ORB_ID);

    return myORB.getORBData().getORBId();
  }

  synchronized public String[] adapter_name() {
    checkAccess(MID_ADAPTER_NAME);

    if (adapterName == null) {
      checkForNullTemplate();

      ObjectAdapterId oaid = oktemp.getObjectAdapterId();
      adapterName = oaid.getAdapterName();
    }

    return adapterName;
  }

  /**
   * The opaque identifier for the object adapter.
   */
  synchronized public byte[] adapter_id() {
    checkAccess(MID_ADAPTER_ID);

    if (adapterId == null) {
      checkForNullTemplate();
      adapterId = oktemp.getAdapterId();
    }

    return adapterId;
  }

  /**
   * The RepositoryID for the most derived interface of the servant.
   */
  public String target_most_derived_interface() {
    checkAccess(MID_TARGET_MOST_DERIVED_INTERFACE);
    return targetMostDerivedInterface;
  }

  /**
   * Returns the policy in effect for this operation for the given policy
   * type.
   */
  public Policy get_server_policy(int type) {
    // access is currently valid for all states:
    //checkAccess( MID_GET_SERVER_POLICY );

    Policy result = null;

    if (objectAdapter != null) {
      result = objectAdapter.getEffectivePolicy(type);
    }

    // _REVISIT_ RTF Issue: get_server_policy spec not in sync with
    // get_effective_policy spec.

    return result;
  }

  /**
   * Allows an Interceptor to set a slot in the Current that is in the scope
   * of the request.  If data already exists in that slot, it will be
   * overwritten.  If the ID does not define an allocated slot, InvalidSlot
   * is raised.
   */
  public void set_slot(int id, Any data) throws InvalidSlot {
    // access is currently valid for all states:
    //checkAccess( MID_SET_SLOT );

    slotTable.set_slot(id, data);
  }

  /**
   * Returns true if the servant is the given RepositoryId, false if it is
   * not.
   */
  public boolean target_is_a(String id) {
    checkAccess(MID_TARGET_IS_A);

    boolean result = false;
    if (servant instanceof Servant) {
      result = ((Servant) servant)._is_a(id);
    } else if (StubAdapter.isStub(servant)) {
      result = ((org.omg.CORBA.Object) servant)._is_a(id);
    } else {
      throw wrapper.servantInvalid();
    }

    return result;
  }

  /**
   * Allows Interceptors to add service contexts to the request.
   */
  public void add_reply_service_context(ServiceContext service_context,
      boolean replace) {
    // access is currently valid for all states:
    //checkAccess( MID_ADD_REPLY_SERVICE_CONTEXT );

    if (currentExecutionPoint == EXECUTION_POINT_ENDING) {
      ServiceContexts scs = replyMessage.getServiceContexts();

      // May be null.  If this is null, create a new one in its place.
      if (scs == null) {
        scs = new ServiceContexts(myORB);
        replyMessage.setServiceContexts(scs);
      }

      if (cachedReplyServiceContexts == null) {
        cachedReplyServiceContexts = new HashMap();
      }

      // This is during and ending point, so we now have enough
      // information to add the reply service context.
      addServiceContext(cachedReplyServiceContexts, scs,
          service_context, replace);
    }

    // We enqueue all adds for the following reasons:
    //
    // If we are not in the ending point then we do not yet have a
    // pointer to the ServiceContexts object so we cannot access the
    // service contexts until we get to the ending point.
    // So we enqueue this add reply service context request.
    // It is added when we do have a handle on the service contexts object.
    //
    // If we are in the ending point and we just add directly to the
    // SC container but then an interceptor raises a SystemException
    // then that add will be lost since a new container is created
    // for the SystemException response.
    //
    // Therefore we always enqueue and never dequeue (per request) so
    // that all adds will be completed.

    AddReplyServiceContextCommand addReply =
        new AddReplyServiceContextCommand();
    addReply.service_context = service_context;
    addReply.replace = replace;

    if (addReplyServiceContextQueue == null) {
      addReplyServiceContextQueue = new ArrayList();
    }

    // REVISIT: this does not add to the cache.
    enqueue(addReply);
  }

  // NOTE: When adding a method, be sure to:
  // 1. Add a MID_* constant for that method
  // 2. Call checkAccess at the start of the method
  // 3. Define entries in the validCall[][] table for interception points.

    /*
     **********************************************************************
     * Public RequestInfo interfaces
     *
     * These are implemented here because they have differing
     * implementations depending on whether this is a client or a server
     * request info object.
     **********************************************************************/

  /**
   * See ServerRequestInfo for javadocs.
   */
  public int request_id() {
    // access is currently valid for all states:
    //checkAccess( MID_REQUEST_ID );
        /*
         * NOTE: The request id in server interceptors is NOT the
         * same as the GIOP request id.  The ORB may be servicing several
         * connections, each with possibly overlapping sets of request ids.
         * Therefore we create a request id specific to interceptors.
         */
    return serverRequestId;
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public String operation() {
    // access is currently valid for all states:
    //checkAccess( MID_OPERATION );
    return request.getOperationName();
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public Parameter[] arguments() {
    checkAccess(MID_ARGUMENTS);

    if (cachedArguments == null) {
      if (!isDynamic) {
        throw stdWrapper.piOperationNotSupported1();
      }

      if (dsiArguments == null) {
        throw stdWrapper.piOperationNotSupported8();
      }

      // If it is a DSI request then get the arguments from the DSI req
      // and convert that into parameters.
      cachedArguments = nvListToParameterArray(dsiArguments);
    }

    // Good citizen: In the interest of efficiency, we assume
    // interceptors will be "good citizens" in that they will not
    // modify the contents of the Parameter[] array.  We also assume
    // they will not change the values of the containing Anys.

    return cachedArguments;
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public TypeCode[] exceptions() {
    checkAccess(MID_EXCEPTIONS);

    // _REVISIT_ PI RTF Issue: No exception list on server side.

    throw stdWrapper.piOperationNotSupported2();
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public String[] contexts() {
    checkAccess(MID_CONTEXTS);

    // We do not support this because our ORB does not send contexts.

    throw stdWrapper.piOperationNotSupported3();
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public String[] operation_context() {
    checkAccess(MID_OPERATION_CONTEXT);

    // We do not support this because our ORB does not send
    // operation_context.

    throw stdWrapper.piOperationNotSupported4();
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public Any result() {
    checkAccess(MID_RESULT);

    if (!isDynamic) {
      throw stdWrapper.piOperationNotSupported5();
    }

    if (dsiResult == null) {
      throw wrapper.piDsiResultIsNull();
    }

    // Good citizen: In the interest of efficiency, we assume that
    // interceptors will not modify the contents of the result Any.
    // Otherwise, we would need to create a deep copy of the Any.

    return dsiResult;
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public boolean response_expected() {
    // access is currently valid for all states:
    //checkAccess( MID_RESPONSE_EXPECTED );
    return !request.isOneWay();
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public Object forward_reference() {
    checkAccess(MID_FORWARD_REFERENCE);
    // Check to make sure we are in LOCATION_FORWARD
    // state as per ptc/00-08-06, table 21-2
    // footnote 2.
    if (replyStatus != LOCATION_FORWARD.value) {
      throw stdWrapper.invalidPiCall1();
    }

    return getForwardRequestException().forward;
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public org.omg.IOP.ServiceContext get_request_service_context(int id) {
    checkAccess(MID_GET_REQUEST_SERVICE_CONTEXT);

    if (cachedRequestServiceContexts == null) {
      cachedRequestServiceContexts = new HashMap();
    }

    return getServiceContext(cachedRequestServiceContexts,
        request.getRequestServiceContexts(), id);
  }

  /**
   * See ServerRequestInfo for javadocs.
   */
  public org.omg.IOP.ServiceContext get_reply_service_context(int id) {
    checkAccess(MID_GET_REPLY_SERVICE_CONTEXT);

    if (cachedReplyServiceContexts == null) {
      cachedReplyServiceContexts = new HashMap();
    }

    return getServiceContext(cachedReplyServiceContexts,
        replyMessage.getServiceContexts(), id);
  }

    /*
     **********************************************************************
     * Private-scope classes and methods
     **********************************************************************/

  // A command encapsulating a request to add a reply service context.
  // These commands are enqueued until we have a handle on the actual
  // reply service context, at which point they are executed.
  private class AddReplyServiceContextCommand {

    ServiceContext service_context;
    boolean replace;
  }

  // Adds the given add reply service context command to the queue of
  // such commands.  If a command is detected to have the same id as
  // the service context in this command, and replace is false,
  // BAD_INV_ORDER is thrown.  If replace is true, the original command
  // in the queue is replaced by this command.
  private void enqueue(AddReplyServiceContextCommand addReply) {
    int size = addReplyServiceContextQueue.size();
    boolean found = false;

    for (int i = 0; i < size; i++) {
      AddReplyServiceContextCommand cmd =
          (AddReplyServiceContextCommand)
              addReplyServiceContextQueue.get(i);

      if (cmd.service_context.context_id ==
          addReply.service_context.context_id) {
        found = true;
        if (addReply.replace) {
          addReplyServiceContextQueue.set(i, addReply);
        } else {
          throw stdWrapper.serviceContextAddFailed(
              new Integer(cmd.service_context.context_id));
        }
        break;
      }
    }

    if (!found) {
      addReplyServiceContextQueue.add(addReply);
    }
  }

    /*
     **********************************************************************
     * Package and protected-scope methods
     **********************************************************************/

  /**
   * Overridden from RequestInfoImpl.  This version calls the super
   * and then, if we are changing to ending points, executes all
   * enqueued AddReplyServiceContextCommands.
   */
  protected void setCurrentExecutionPoint(int executionPoint) {
    super.setCurrentExecutionPoint(executionPoint);

    // If we are transitioning to ending point, we will now have a pointer
    // to the reply service contexts, so we can execute all queued
    // add reply service context requests.
    if ((executionPoint == EXECUTION_POINT_ENDING) &&
        (addReplyServiceContextQueue != null)) {
      int size = addReplyServiceContextQueue.size();
      for (int i = 0; i < size; i++) {
        AddReplyServiceContextCommand addReply =
            (AddReplyServiceContextCommand)
                addReplyServiceContextQueue.get(i);
        try {
          add_reply_service_context(addReply.service_context,
              addReply.replace);
        } catch (BAD_INV_ORDER e) {
          // _REVISIT_  The only way this can happen is if during
          // rrsc or rr, the interceptor tried to add with
          // replace=false to a service context that is present in
          // the reply message.  At that time there was no way for
          // us to check for this, so the best we can do is ignore
          // the original request.
        }
      }

      // We specifically do not empty the SC queue so that if
      // the interceptor raises an exception the queued service contexts
      // will be put in the exception response.
    }
  }

  /**
   * Stores the various sources of information used for this info object.
   */
  protected void setInfo(CorbaMessageMediator request, ObjectAdapter oa,
      byte[] objectId, ObjectKeyTemplate oktemp) {
    this.request = request;
    this.objectId = objectId;
    this.oktemp = oktemp;
    this.objectAdapter = oa;
    this.connection = (com.sun.corba.se.spi.legacy.connection.Connection)
        request.getConnection();
  }

  /**
   * Stores the various sources of information used for this info object.
   */
  protected void setDSIArguments(NVList arguments) {
    this.dsiArguments = arguments;
  }

  /**
   * Stores the various sources of information used for this info object.
   */
  protected void setDSIException(Any exception) {
    this.dsiException = exception;

    // Clear cached exception value:
    cachedSendingException = null;
  }

  /**
   * Stores the various sources of information used for this info object.
   */
  protected void setDSIResult(Any result) {
    this.dsiResult = result;
  }

  /**
   * Sets the exception to be returned by received_exception and
   * received_exception_id.
   */
  protected void setException(Exception exception) {
    super.setException(exception);

    // Make sure DSIException is null because this is the more recent one.
    this.dsiException = null;

    // Clear cached exception value:
    cachedSendingException = null;
  }

  /**
   * Stores the various sources of information used for this info object.
   */
  protected void setInfo(java.lang.Object servant,
      String targetMostDerivedInterface) {
    this.servant = servant;
    this.targetMostDerivedInterface = targetMostDerivedInterface;
    this.isDynamic =
        (servant instanceof
            org.omg.PortableServer.DynamicImplementation) ||
            (servant instanceof org.omg.CORBA.DynamicImplementation);
  }

  /**
   * Set reply message
   */
  void setReplyMessage(ReplyMessage replyMessage) {
    this.replyMessage = replyMessage;
  }

  /**
   * Overridden from RequestInfoImpl.  Calls the super class, then
   * sets the ending point call depending on the reply status.
   */
  protected void setReplyStatus(short replyStatus) {
    super.setReplyStatus(replyStatus);
    switch (replyStatus) {
      case SUCCESSFUL.value:
        endingPointCall = CALL_SEND_REPLY;
        break;
      case SYSTEM_EXCEPTION.value:
      case USER_EXCEPTION.value:
        endingPointCall = CALL_SEND_EXCEPTION;
        break;
      case LOCATION_FORWARD.value:
      case TRANSPORT_RETRY.value:
        endingPointCall = CALL_SEND_OTHER;
        break;
    }
  }

  /**
   * Release the servant object so the user has control over its lifetime.
   * Called after receive_request is finished executing.
   */
  void releaseServant() {
    this.servant = null;
  }

  /**
   * Sets the forwardRequestRaisedInEnding flag to true, indicating that
   * a server ending point has raised location forward at some point.
   */
  void setForwardRequestRaisedInEnding() {
    this.forwardRequestRaisedInEnding = true;
  }

  /**
   * Returns true if ForwardRequest was raised by a server ending point
   * or false otherwise.
   */
  boolean isForwardRequestRaisedInEnding() {
    return this.forwardRequestRaisedInEnding;
  }

  /**
   * Returns true if this is a dynamic invocation, or false if not
   */
  boolean isDynamic() {
    return this.isDynamic;
  }

  /**
   * See description for RequestInfoImpl.checkAccess
   */
  protected void checkAccess(int methodID) {
    // Make sure currentPoint matches the appropriate index in the
    // validCall table:
    int validCallIndex = 0;
    switch (currentExecutionPoint) {
      case EXECUTION_POINT_STARTING:
        validCallIndex = 0;
        break;
      case EXECUTION_POINT_INTERMEDIATE:
        validCallIndex = 1;
        break;
      case EXECUTION_POINT_ENDING:
        switch (endingPointCall) {
          case CALL_SEND_REPLY:
            validCallIndex = 2;
            break;
          case CALL_SEND_EXCEPTION:
            validCallIndex = 3;
            break;
          case CALL_SEND_OTHER:
            validCallIndex = 4;
            break;
        }
        break;
    }

    // Check the validCall table:
    if (!validCall[methodID][validCallIndex]) {
      throw stdWrapper.invalidPiCall2();
    }
  }

}
